﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting.Lifetime;
using System.Text;
using System.Threading.Tasks;

namespace PlutoStudio
{
    /// <summary>
    /// 隔离应用程序域加载器。
    /// </summary>
    public class IsolateDomainLoader : IDisposable
    {
        private AppDomain Domain;
        /// <summary>
        /// 创建 <see cref="IsolateDomainLoader"/> 对象的实例。
        /// </summary>
        /// <param name="path">应用程序域基础路径。</param>
        /// <param name="configFileName">配置文件名。</param>
        public IsolateDomainLoader(string path, string configFileName = "")
        {
            AppDomainSetup setup = new AppDomainSetup();
            setup.ApplicationName = "IsolateDomainLoader";
            setup.ApplicationBase = path;
            setup.DynamicBase = path;
            setup.PrivateBinPath = path;
            setup.CachePath = setup.ApplicationBase;
            setup.ShadowCopyFiles = "true";
            setup.ShadowCopyDirectories = setup.ApplicationBase;
            if (!string.IsNullOrWhiteSpace(configFileName))
            {
                setup.ConfigurationFile = configFileName;
                setup.ConfigurationFile = Path.Combine(path, configFileName);
            }
            Domain = AppDomain.CreateDomain("ApplicationLoaderDomain", AppDomain.CurrentDomain.Evidence, setup);
        }

        /// <summary>
        /// 根据应用程序集及类名获取获取 <see cref="RemoteObject"/> 代理实例。
        /// </summary>
        /// <param name="assemblyFile">应用程序集名</param>
        /// <param name="typeFullName">类完全限定名</param>
        /// <returns><see cref="RemoteObject"/> 代理对象实例</returns>
        public RemoteObject GetObject(string assemblyFile, string typeFullName)
        {
            string name = Assembly.GetExecutingAssembly().FullName;
            var obj = (RemoteObject)this.Domain.CreateInstanceFromAndUnwrap(Assembly.GetExecutingAssembly().Location, typeof(RemoteObject).FullName);
            obj.Init(assemblyFile, typeFullName);
            ILease lease = (ILease)obj.GetLifetimeService();
            lease.Register(obj);
            return obj;
        }

        /// <summary>
        /// 卸载应用程序域。
        /// </summary>
        public void Unload()
        {
            if (Domain == null)
                return;
            try
            {
                AppDomain.Unload(this.Domain);
                Domain = null;
            }
            catch
            {
            }
        }

        /// <summary>
        /// 析构对象实例。
        /// </summary>
        ~IsolateDomainLoader()
        {
            Dispose(false);
        }

        /// <summary>
        /// 实现IDisposable中的Dispose方法
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 执行与释放或重置非托管资源关联的应用程序定义的任务。
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                Unload();
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    Console.WriteLine("Domain Unloaded");
                }
            }
        }
    }
}
